// @Author EthanScriptOn
// @Desc
package context

import (
	"errors"
	"fmt"
	"gitee.com/fatzeng/srf_switch_basic_components/resource"
	"gitee.com/fatzeng/srf_switch_basic_components/tool"
	"sync"
	"sync/atomic"
	"time"
)

// SrfContext The basic context provides the ability to cancel,
// set the fetch value, get the error fetch, and set the sub-context
type SrfContext struct {
	mu       sync.RWMutex
	done     atomic.Value
	err      error
	timer    *time.Timer
	deadline time.Time
	child    map[string]SrfContextInterface
	settings *resource.ResourceManager
}

// GenerateSrfContextWithDeadLine A context object with an end time
func GenerateSrfContextWithDeadLine(deadLine time.Time) *SrfContext {
	srfContext := GenerateSrfContext()
	srfContext.deadline = deadLine
	deadLineDuration := time.Until(deadLine)
	if deadLineDuration <= 0 || deadLine.Before(time.Now()) {
		srfContext.err = srfContext.CancelAll()
		return srfContext
	}
	srfContext.timer = time.AfterFunc(deadLineDuration, func() {
		srfContext.err = srfContext.CancelAll()
	})
	return srfContext
}

func GenerateSrfContext() *SrfContext {
	return &SrfContext{
		mu:       sync.RWMutex{},
		done:     atomic.Value{},
		child:    make(map[string]SrfContextInterface),
		settings: resource.GenerateResourceManager(),
	}
}

// WithChild Set the child context object
func (s *SrfContext) WithChild(childCtxName string, childCtx SrfContextInterface) error {
	return tool.LockWithSync(s.mu.Lock, s.mu.Unlock, func() error {
		s.child[childCtxName] = childCtx
		return nil
	})
}

// GetChild Get the child context object
func (s *SrfContext) GetChild(childCtxName string) (SrfContextInterface, error) {
	var resp SrfContextInterface
	return resp, tool.LockWithSync(s.mu.Lock, s.mu.Unlock, func() error {
		resp = s.child[childCtxName]
		return nil
	})
}

// Set Value setting: ResourceManager provides the ability to manage values
func (s *SrfContext) Set(key, value interface{}) error {
	return s.settings.OperateResource(&resource.ResourceOptions{
		DoExecute: func(container map[interface{}]interface{}) error {
			container[key] = value
			return nil
		},
	})
}

// Get Value acquisition: ResourceManager provides the ability to manage values
func (s *SrfContext) Get(key interface{}) (value interface{}, err error) {
	resource, err, hasResource := s.settings.GetResource(key)
	if err != nil {
		return nil, err
	}
	if !hasResource {
		return nil, nil
	}
	return resource, nil
}

// GetSettings Expose the explorer
func (s *SrfContext) GetSettings() (settings *resource.ResourceManager) {
	return s.settings
}

// Cancel Contextual cancellation
func (s *SrfContext) Cancel() (err error) {
	return tool.LockWithSync(s.mu.Lock, s.mu.Unlock, func() error {
		done := s.getDone()
		if done != nil {
			close(done)
			return nil
		}
		if s.timer != nil {
			s.timer.Stop()
			return nil
		}
		return errors.New("failed to load cancel channel")
	})
}

// CancelAll Cancel all contexts
func (s *SrfContext) CancelAll() (err error) {
	if err = s.Cancel(); err != nil {
		return
	}
	for childName, childCtx := range s.child {
		if err = childCtx.Cancel(); err != nil {
			return fmt.Errorf("exception occurred while canceling sub: %v config_context: %v", childName, err)
		}
	}
	return
}

// Deadline Return to the deadline for death
func (s *SrfContext) Deadline() (deadline time.Time, ok bool) {
	return s.deadline, true
}

// Done Get the end channel, and when the context is canceled, it will be notified by this channel
func (s *SrfContext) Done() <-chan struct{} {
	return s.getDone()
}

func (s *SrfContext) getDone() chan struct{} {
	d := s.done.Load()
	if d != nil {
		return d.(chan struct{})
	}
	_ = tool.LockWithSync(s.mu.Lock, s.mu.Unlock, func() error {
		d = s.done.Load()
		if d == nil {
			d = make(chan struct{})
			s.done.Store(d)
		}
		return nil
	})
	return d.(chan struct{})
}

func (s *SrfContext) Err() error {
	return tool.LockWithSync(s.mu.Lock, s.mu.Unlock, func() error {
		return s.err
	})
}

func (s *SrfContext) Value(key interface{}) interface{} {
	_, value := s.Get(key)
	return value
}

// Contains Whether it is included
func (s *SrfContext) Contains(key interface{}) (bool, error) {
	_, err, hasResource := s.settings.GetResource(key)
	if err != nil {
		return false, err
	}
	return hasResource, nil
}
